#include <gtest/gtest.h>
#include <gmock/gmock.h>
#include "uniqueptr.hpp"

// 运行测试，确保环境正常后进入正式迭代。
TEST(UniquePtrInitialTest, Compiles) {
    UniquePtr<int> ptr;
    (void)ptr;
}

// 目前删除器先简单保存为 `nullptr`，稍后再完善。
TEST(UniquePtrTest, DefaultConstructedIsNull) {
    UniquePtr<int> ptr;
    EXPECT_EQ(ptr.get(), nullptr);
    EXPECT_FALSE(ptr);
}

TEST(UniquePtrTest, NullptrConstructionBehavesLikeDefault) {
    UniquePtr<int> ptr(nullptr);
    EXPECT_EQ(ptr.get(), nullptr);
    EXPECT_FALSE(ptr);
}

TEST(UniquePtrTest, ConstructWithRawPointerAndAccess) {
    UniquePtr<std::string> ptr(new std::string("hello"));
    EXPECT_TRUE(ptr);
    EXPECT_EQ(ptr.get()->size(), 5U);
    EXPECT_EQ((*ptr)[1], 'e');
    EXPECT_EQ(ptr.operator->()->substr(0, 4), "hell");
}

struct Counter {
    static inline int instances = 0;
    Counter() { ++instances; }
    ~Counter() { --instances; }
};

TEST(UniquePtrTest, ReleasesResourceOnDestruction) {
    EXPECT_EQ(Counter::instances, 0);
    {
        UniquePtr<Counter> ptr(new Counter());
        EXPECT_EQ(Counter::instances, 1);
    }
    EXPECT_EQ(Counter::instances, 0);
}

TEST(UniquePtrTest, ResetReleasesOldAndTakesNew) {
    UniquePtr<int> ptr(new int(10));
    ptr.reset(new int(20));
    ASSERT_NE(ptr.get(), nullptr);
    EXPECT_EQ(*ptr, 20);
}

TEST(UniquePtrTest, ResetWithoutArgReleasesResource) {
    UniquePtr<int> ptr(new int(10));
    ptr.reset();
    EXPECT_EQ(ptr.get(), nullptr);
    EXPECT_FALSE(ptr);
}

TEST(UniquePtrTest, ReleaseReturnsRawPointerWithoutDeleting) {
    UniquePtr<int> ptr(new int(99));
    int* raw = ptr.release();
    EXPECT_EQ(ptr.get(), nullptr);
    EXPECT_FALSE(ptr);
    ASSERT_NE(raw, nullptr);
    EXPECT_EQ(*raw, 99);
    delete raw;
}

TEST(UniquePtrTest, SwapExchangesPointers) {
    UniquePtr<int> a(new int(1));
    UniquePtr<int> b(new int(2));

    a.swap(b);
    EXPECT_EQ(*a, 2);
    EXPECT_EQ(*b, 1);

    swap(a, b);
    EXPECT_EQ(*a, 1);
    EXPECT_EQ(*b, 2);
}

TEST(UniquePtrTest, MoveConstructionTransfersOwnership) {
    UniquePtr<int> src(new int(5));
    UniquePtr<int> dst(std::move(src));

    EXPECT_EQ(src.get(), nullptr);
    EXPECT_FALSE(src);
    ASSERT_NE(dst.get(), nullptr);
    EXPECT_EQ(*dst, 5);
}

TEST(UniquePtrTest, MoveAssignmentTransfersOwnership) {
    UniquePtr<int> src(new int(42));
    UniquePtr<int> dst(new int(1));

    dst = std::move(src);

    EXPECT_EQ(src.get(), nullptr);
    EXPECT_FALSE(src);
    ASSERT_NE(dst.get(), nullptr);
    EXPECT_EQ(*dst, 42);
}

class MockDeleter {
public:
    MOCK_METHOD(void, Call, (int*));
};

static MockDeleter* g_mockDeleter = nullptr;

void ForwardingDeleter(int* ptr) {
    if (g_mockDeleter) {
        g_mockDeleter->Call(ptr);
    }
    delete ptr;
}

TEST(UniquePtrTest, CustomDeleterIsInvokedOnDestruction) {
    auto* raw = new int(7);
    testing::StrictMock<MockDeleter> mock;
    g_mockDeleter = &mock;

    EXPECT_CALL(mock, Call(raw)).Times(1);

    {
        UniquePtr<int> ptr(raw, ForwardingDeleter);
        EXPECT_EQ(ptr.get(), raw);
    }

    g_mockDeleter = nullptr;
}

TEST(UniquePtrTest, GetDeleterReturnsCurrentDeleter) {
    UniquePtr<int> ptr;
    auto deleter = ptr.get_deleter();
    EXPECT_NE(deleter, nullptr);
    int* raw = new int(3);
    deleter(raw);
}

TEST(UniquePtrTest, MakeUniqueConstructsObject) {
    auto ptr = make_unique<std::pair<int, std::string>>(7, "seven");
    ASSERT_TRUE(ptr);
    EXPECT_EQ(ptr->first, 7);
    EXPECT_EQ(ptr->second, "seven");
}